#include "CameraPrecompiled.hxx"
#include "CameraModelView.hxx"

Void Eifersucht::ModelViewerCamera::Reset()
{
	BasicCamera::_Basic_camera_reset();

	m_f4x4World = DirectX::XMMatrixIdentity();
	m_f4x4ModelRotation = DirectX::XMMatrixIdentity();
	m_f4x4ModelLastRotation = DirectX::XMMatrixIdentity();
	m_f4x4CameraLastRotation = DirectX::XMMatrixIdentity();

	m_fRadius = m_fDefaultRadius;
	m_lWorldArcball.Reset();
	m_lViewArcball.Reset();
}

Eifersucht::ModelViewerCamera::ModelViewerCamera() : 
	BasicCamera()
{
	m_f3ModelCenter = DirectX::XMVectorZero();
	m_f4x4World = DirectX::XMMatrixIdentity();
	m_f4x4ModelRotation = DirectX::XMMatrixIdentity();
	m_f4x4ModelLastRotation = DirectX::XMMatrixIdentity();
	m_f4x4CameraLastRotation = DirectX::XMMatrixIdentity();

	SetRadius();
	SetButtons();
	SetLimitPitch(false);
	SetAttachCameraToModel(false);

	m_bEnableMovement = true;
	m_bDragSinceLastUpdate = true;
}

Void Eifersucht::ModelViewerCamera::SetDragRect( _In_ RectConstRef l4Drag )
{
	BasicCamera::SetDragRect(l4Drag);

	m_lWorldArcball.SetOffset( _To_int l4Drag.left, _To_int l4Drag.top );
	m_lViewArcball.SetOffset( _To_int l4Drag.left, _To_int l4Drag.top );

	SetWindow( _To_int (l4Drag.right - l4Drag.left), _To_int (l4Drag.bottom - l4Drag.top) );
}

Void Eifersucht::ModelViewerCamera::SetView( _In_ Float32x3Ptr pf3Eye, _In_ Float32x3Ptr pf3LookAt )
{
	BasicCamera::_Basic_camera_view(pf3Eye, pf3LookAt);

	Float32x4x4Const f4x4Rotation = DirectX::XMMatrixLookAtLH((*pf3Eye), (*pf3LookAt), Float32x3Const( 0.0f, 1.0f, 0.0f ));
	QuaternionConst f4Quat = DirectX::XMQuaternionRotationMatrix( f4x4Rotation );
	m_lViewArcball.SetNowQuat(f4Quat);

	Float32x3Const f3EyeToFocus = m_f3LookAt - m_f3Eye;
	Float32Const fLen = f3EyeToFocus.Length();
	SetRadius(fLen);

	m_bDragSinceLastUpdate = true;
}

Void Eifersucht::ModelViewerCamera::OnFrameMove( _In_ Float32 fElapsed )
{
	if ((!m_bDragSinceLastUpdate) && (!m_iKeysPressed)) return;
	if (IsKeyPressed( _To_byte KeyboardButton::Reset )) Reset();

	m_bDragSinceLastUpdate = false;
	_Basic_camera_input(m_bEnableMovement, m_iButtonMask);
	//_Basic_camera_input(true, true);
	_Basic_camera_velocity(fElapsed);


	if ( m_iPointerWheelDelta )
	{
		m_fRadius -= m_iPointerWheelDelta * m_fRadius * 0.1f / 120.0f; //> Merge 0.1 120
	}

	m_fRadius = MinOf( m_fMaxRadius, m_fRadius );
	m_fRadius = MaxOf( m_fMinRadius, m_fRadius );
	m_iPointerWheelDelta = 0;

	Float32x4x4Const f4x4CameraRotation = DirectX::XMMatrixInverse(
		nullptr, (*m_lViewArcball.GetRotationMatrix())
		);

	Float32x3Const f3LclUp(0.0f, 1.0f, 0.0f);
	Float32x3Const f3LclAhead(0.0f, 0.0f, 1.0f);
	Float32x3Const f3WorldUp = DirectX::XMVector3TransformCoord(f3LclUp, f4x4CameraRotation);
	Float32x3Const f3WorldAhead = DirectX::XMVector3TransformCoord(f3LclAhead, f4x4CameraRotation);

	Float32x3Const f3PositionDelta = m_f3Velocity * fElapsed;
	Float32x3Const f3PositionDeltaWorld = DirectX::XMVector3TransformCoord(
		f3PositionDelta, f4x4CameraRotation
		);

	m_f3LookAt += f3PositionDeltaWorld;
	if ( m_bClipToBoundary ) _Basic_camera_constraine(&m_f3LookAt);

	m_f3Eye = m_f3LookAt - f3WorldAhead * m_fRadius;
	m_f4x4View = DirectX::XMMatrixLookAtLH(m_f3Eye, m_f3LookAt, f3WorldUp);

	Float32x4x4Const f4x4InverseModelLastRotation = DirectX::XMMatrixInverse(nullptr, m_f4x4ModelRotation); 
	Float32x4x4 f4x4InverseView = DirectX::XMMatrixInverse(nullptr, m_f4x4View);
	f4x4InverseView._41 = f4x4InverseView._42 = f4x4InverseView._43 = 0.0f;

	Float32x4x4 f4x4ModelRotation = (*m_lWorldArcball.GetRotationMatrix());
	m_f4x4ModelRotation *= m_f4x4View * 
		f4x4InverseModelLastRotation * 
		f4x4ModelRotation * 
		f4x4InverseView;

	if ((m_lViewArcball.IsBeingDragged()) && 
		(m_bAttachCameraToModel) && 
		(!IsKeyPressed( _To_byte KeyboardButton::Ctrl )))
	{
		Float32x4x4Const f4x4inverseCameraLastRotation = DirectX::XMMatrixInverse( nullptr, m_f4x4CameraLastRotation);
		Float32x4x4Const f4x4CameraRotationDelta = f4x4inverseCameraLastRotation * f4x4CameraRotation;
		m_f4x4ModelRotation *= f4x4CameraRotationDelta;
	}

	m_f4x4CameraLastRotation = f4x4CameraRotation;
	m_f4x4ModelLastRotation = f4x4ModelRotation;

	DirectX::XMVECTOR *pf4XBasis = (DirectX::XMVECTOR *)(&m_f4x4ModelRotation._41);
	DirectX::XMVECTOR *pf4YBasis = (DirectX::XMVECTOR *)(&m_f4x4ModelRotation._42);
	DirectX::XMVECTOR *pf4ZBasis = (DirectX::XMVECTOR *)(&m_f4x4ModelRotation._43);

	(*pf4XBasis) = DirectX::XMVector3Normalize((*pf4XBasis));
	(*pf4YBasis) = DirectX::XMVector3Cross((*pf4ZBasis), (*pf4XBasis));
	(*pf4YBasis) = DirectX::XMVector3Normalize((*pf4YBasis));
	(*pf4ZBasis) = DirectX::XMVector3Cross((*pf4XBasis), (*pf4YBasis));

	m_f4x4ModelRotation._41 = m_f3LookAt.x;
	m_f4x4ModelRotation._42 = m_f3LookAt.y;
	m_f4x4ModelRotation._43 = m_f3LookAt.z;

	Float32x4x4Const f4x4Translation = DirectX::XMMatrixTranslation( 
		-m_f3ModelCenter.x, -m_f3ModelCenter.y, -m_f3ModelCenter.z
		);

	m_f4x4World = f4x4Translation * m_f4x4ModelRotation;
}

Void Eifersucht::ModelViewerCamera::OnCaptureChanged()
{
	_Basic_camera_on_pointer_capture_changed();

	m_lWorldArcball.OnDragStop();
	m_lViewArcball.OnDragStop();
}

Void Eifersucht::ModelViewerCamera::OnPointerWheelChanged( _In_ Int16 iWheelDelta )
{
	m_bDragSinceLastUpdate = true;
	_Basic_camera_on_pointer_wheel_changed( iWheelDelta );
}

Void Eifersucht::ModelViewerCamera::OnPointerMoved( _In_ Int16 iX, _In_ Int16 iY )
{
	m_bDragSinceLastUpdate = true;
	m_lWorldArcball.OnDragContinue( iX, iY );
	m_lViewArcball.OnDragContinue( iX, iY );
}

Void Eifersucht::ModelViewerCamera::OnPointerLeftButtonPressed( _In_ Int16 iX, _In_ Int16 iY )
{
	static ByteConst s_eBtn = _To_byte PointerButton::Left;

	//PrintToDebugWindowOnly8( "Eifersucht::ModelViewerCamera: Left pressed." );

	m_bDragSinceLastUpdate = true;
	_Basic_camera_on_pointer_left_pressed( iX, iY );
	if ( m_iRotateModelButtonMask & s_eBtn ) m_lWorldArcball.OnDragStart( iX, iY );
	if ( m_iRotateCameraButtonMask & s_eBtn ) m_lViewArcball.OnDragStart( iX, iY );
	_Basic_camera_post_on_pointer_pressed();
}

Void Eifersucht::ModelViewerCamera::OnPointerRightButtonPressed( _In_ Int16 iX, _In_ Int16 iY )
{
	static ByteConst s_eBtn = _To_byte PointerButton::Right;

	//PrintToDebugWindowOnly8( "Eifersucht::ModelViewerCamera: Right pressed." );

	m_bDragSinceLastUpdate = true;
	_Basic_camera_on_pointer_right_pressed( iX, iY );
	if ( m_iRotateModelButtonMask & s_eBtn ) m_lWorldArcball.OnDragStart( iX, iY );
	if ( m_iRotateCameraButtonMask & s_eBtn ) m_lViewArcball.OnDragStart( iX, iY );
	_Basic_camera_post_on_pointer_pressed();
}

Void Eifersucht::ModelViewerCamera::OnPointerMiddleButtonPressed( _In_ Int16 iX, _In_ Int16 iY )
{
	static ByteConst s_eBtn = _To_byte PointerButton::Middle;

	//PrintToDebugWindowOnly8( "Eifersucht::ModelViewerCamera: Middle pressed." );

	m_bDragSinceLastUpdate = true;
	_Basic_camera_on_pointer_middle_pressed( iX, iY );
	if ( m_iRotateModelButtonMask & s_eBtn ) m_lWorldArcball.OnDragStart( iX, iY );
	if ( m_iRotateCameraButtonMask & s_eBtn ) m_lViewArcball.OnDragStart( iX, iY );
	_Basic_camera_post_on_pointer_pressed();
}

Void Eifersucht::ModelViewerCamera::OnPointerLeftButtonReleased()
{
	static ByteConst s_eBtn = _To_byte PointerButton::Left;

	//PrintToDebugWindowOnly8( "Eifersucht::ModelViewerCamera: Left released." );

	m_bDragSinceLastUpdate = true;
	_Basic_camera_on_pointer_left_released();
	if ( m_iRotateModelButtonMask & s_eBtn ) m_lWorldArcball.OnDragStop();
	if ( m_iRotateCameraButtonMask & s_eBtn ) m_lViewArcball.OnDragStop();
	_Basic_camera_post_on_pointer_released();
}

Void Eifersucht::ModelViewerCamera::OnPointerRightButtonReleased()
{
	static ByteConst s_eBtn = _To_byte PointerButton::Right;

	//PrintToDebugWindowOnly8( "Eifersucht::ModelViewerCamera: Right released." );

	m_bDragSinceLastUpdate = true;
	_Basic_camera_on_pointer_right_released();
	if ( m_iRotateModelButtonMask & s_eBtn ) m_lWorldArcball.OnDragStop();
	if ( m_iRotateCameraButtonMask & s_eBtn ) m_lViewArcball.OnDragStop();
	_Basic_camera_post_on_pointer_released();
}

Void Eifersucht::ModelViewerCamera::OnPointerMiddleButtonReleased()
{
	static ByteConst s_eBtn = _To_byte PointerButton::Middle;

	//PrintToDebugWindowOnly8( "Eifersucht::ModelViewerCamera: Middle released." );

	m_bDragSinceLastUpdate = true;
	_Basic_camera_on_pointer_middle_released();
	if ( m_iRotateModelButtonMask & s_eBtn ) m_lWorldArcball.OnDragStop();
	if ( m_iRotateCameraButtonMask & s_eBtn ) m_lViewArcball.OnDragStop();
	_Basic_camera_post_on_pointer_released();
}
